department-of-veterans-affairs/vets-website

View on GitHub
src/applications/pact-act/utilities/answer-cleanup.js

Summary

Maintainability
A
2 hrs
Test Coverage
// If a Veteran is navigating through the flow, then changes a response to a previous answer that affects
// the display of questions after it, the questions that will no longer show should have their answers
// removed from the Redux store. These utilities take care of that.

import { displayConditionsMet, makeRoadmap } from './display-logic-questions';
import { SHORT_NAME_MAP } from '../constants/question-data-map';
import { getNonNullShortNamesFromStore } from './shared';

/**
 * Filter object for a given value (predicate true/false function)
 */
Object.filter = (obj, predicate) =>
  Object.keys(obj)
    .filter(key => predicate(obj[key]))
    .reduce((res, key) => Object.assign(res, { [key]: obj[key] }), {});

/**
 * Use ROADMAP to determine if a question SHORT_NAME comes after the current question
 * Not flow-specific (e.g. 1989 or earlier)
 *
 * @param {string} shortName - name for question to evaluate
 * @param {string} currentQuestionShortName
 * @param {object} roadmap - all questions shown in the flow per SERVICE_PERIOD response
 */
const questionIsAfterCurrent = (shortName, currentQuestionShortName, roadmap) =>
  roadmap?.indexOf(shortName) > roadmap?.indexOf(currentQuestionShortName);

/** ================================================================
 * Gathers SHORT_NAMEs with responses in the store that
 * are not in the same SERVICE_PERIOD flow
 * Does not gather current question or anything before it
 *
 * @param {array} nonNullShortNames - SHORT_NAMEs in store with non-null answers (regardless of flow)
 * @param {array} flowSpecificQuestions - SHORT_NAMEs that appear in the same SERVICE_PERIOD flow
 */
export const gatherWrongFlowQuestions = (
  nonNullShortNames,
  flowSpecificQuestions,
) => {
  return nonNullShortNames.filter(
    shortName =>
      shortName !== SHORT_NAME_MAP.SERVICE_PERIOD &&
      !flowSpecificQuestions.includes(shortName),
  );
};

/** ================================================================
 * Gathers questions in the same SERVICE_PERIOD flow that no longer meet their display conditions
 *
 * @param {array} nonNullShortNames - SHORT_NAMEs in store with non-null answers (regardless of flow)
 * @param {string} currentQuestionName - SHORT_NAME for question
 * @param {array} questionsToBeNulled - current list of SHORT_NAMEs that need responses nulled
 * @param {object} responsesToClean - answers in the store
 */
export const gatherDCsNotMetQuestions = (
  nonNullShortNames,
  currentQuestionName,
  questionsToBeNulled,
  responsesToClean,
) => {
  const roadmap = makeRoadmap(
    responsesToClean?.[SHORT_NAME_MAP.SERVICE_PERIOD],
  );

  if (roadmap?.length) {
    const dcsNotMet = shortName => {
      return (
        !questionsToBeNulled?.includes(shortName) &&
        questionIsAfterCurrent(shortName, currentQuestionName, roadmap) &&
        !displayConditionsMet(shortName, responsesToClean)
      );
    };

    return nonNullShortNames.filter(shortName => dcsNotMet(shortName));
  }

  return [];
};

/** ================================================================
 * Gathers question SHORT_NAMEs that:
 *   do not display in the current SERVICE_PERIOD flow and/or
 *   no longer meet their display conditions
 *
 * @param {array} nonNullShortName - SHORT_NAMEs in store with non-null answers (regardless of flow)
 * @param {array} flowSpecificQuestions - SHORT_NAMEs that appear in the same SERVICE_PERIOD flow
 * @param {string} currentQuestionName - SHORT_NAME for question
 * @param {object} responsesToClean - answers in the store
 */
export const gatherQuestionsToReset = (
  nonNullShortNames,
  flowSpecificQuestions,
  currentQuestionName,
  responsesToClean,
) => {
  // Start list of SHORT_NAMEs whose answers are no longer needed
  const questionsToBeNulled = [];

  const wrongFlowQuestions = gatherWrongFlowQuestions(
    nonNullShortNames,
    flowSpecificQuestions,
  );

  const dcsNotMetQuestions = gatherDCsNotMetQuestions(
    nonNullShortNames,
    currentQuestionName,
    wrongFlowQuestions,
    responsesToClean,
  );

  if (wrongFlowQuestions?.length) {
    questionsToBeNulled.push(...wrongFlowQuestions);
  }

  if (dcsNotMetQuestions?.length) {
    questionsToBeNulled.push(...dcsNotMetQuestions);
  }

  return questionsToBeNulled;
};

/** ================================================================
 * Starting function for clearing out responses in the store that are not needed
 * for the current flow. Only run when current question's answer has been changed from previous
 *
 * @param {object} responsesInStore - all answers in the store
 * @param {func} updateCleanedFormStore - Redux action to update the answers in the store
 * @param {string} currentQuestionName - SHORT_NAME for question
 */
export const cleanUpAnswers = (
  responsesInStore,
  updateCleanedFormStore,
  currentQuestionName,
) => {
  const responsesToClean = responsesInStore;
  const servicePeriodResponse = responsesInStore?.SERVICE_PERIOD;

  const flowSpecificQuestions = makeRoadmap(servicePeriodResponse);

  const nonNullShortNames = getNonNullShortNamesFromStore(responsesToClean);
  const questionsToBeNulled = gatherQuestionsToReset(
    nonNullShortNames,
    flowSpecificQuestions,
    currentQuestionName,
    responsesToClean,
  );

  for (const question of questionsToBeNulled) {
    if (responsesToClean?.[question]) {
      responsesToClean[question] = null;
    }
  }

  updateCleanedFormStore(responsesToClean);
};