import { describe, it, expect } from "vitest"; import { canonicalRule, ruleMatches, isQuestionVisible, isQuestionRequired, } from "./conditional-rules"; import type { QuestionField } from "./schema-adapter"; describe("Conditional Rules Evaluator", () => { const dummyQuestion: QuestionField = { id: "child.q", title: "Child Question", type: "text", order: 1, required: false, baseRequired: false, isVisible: true, description: "", tooltip: "", extras: { placeHolder: "", range: [0, 0], options: [] }, options: [], }; it("should evaluate single trigger option match correctly", () => { const rule = { parent_question_id: "appearance_health.physical_health_status", trigger_option_ids: [ "appearance_health.physical_health_status.i_have_a_specific_or_chronic_illness", "appearance_health.physical_health_status.i_have_a_physical_deformity_disability_or_limitation", ], operator: "any_of", }; // 1. When parent is not answered expect(ruleMatches(rule, {})).toBe(false); // 2. When parent is answered with non-matching option expect( ruleMatches(rule, { "appearance_health.physical_health_status": { value: "i_am_in_perfect_health", option_id: "appearance_health.physical_health_status.i_am_in_perfect_health", }, }), ).toBe(false); // 3. When parent is answered with matching option (full slug) expect( ruleMatches(rule, { "appearance_health.physical_health_status": { value: "i_have_a_specific_or_chronic_illness", option_id: "appearance_health.physical_health_status.i_have_a_specific_or_chronic_illness", }, }), ).toBe(true); // 4. When parent value only contains the short value expect( ruleMatches(rule, { "appearance_health.physical_health_status": { value: "i_have_a_physical_deformity_disability_or_limitation", }, }), ).toBe(true); }); it("should evaluate complex nested conditions (e.g. number of children rule)", () => { // marital_history.number_of_children: // parent: children_and_guardianship_status (have_children_living_with_me, have_children_not_living_with_me) // conditions: current_marital_status in (divorced_after_living_together, widowed) const rule = { parent_question_id: "marital_history.children_and_guardianship_status", trigger_option_ids: [ "marital_history.children_and_guardianship_status.have_children_living_with_me", "marital_history.children_and_guardianship_status.have_children_not_living_with_me", ], operator: "any_of", conditions: [ { parent_question_id: "marital_history.current_marital_status", trigger_option_ids: [ "marital_history.current_marital_status.divorced_after_living_together", "marital_history.current_marital_status.widowed", ], operator: "any_of", }, ], conditions_operator: "all_of", root_operator: "all_of", }; // Case 1: Single, no children -> false expect( ruleMatches(rule, { "marital_history.current_marital_status": { value: "single_never_married", }, "marital_history.children_and_guardianship_status": { value: "no_children", }, }), ).toBe(false); // Case 2: Divorced, but no children -> false expect( ruleMatches(rule, { "marital_history.current_marital_status": { value: "divorced_after_living_together", option_id: "marital_history.current_marital_status.divorced_after_living_together", }, "marital_history.children_and_guardianship_status": { value: "no_children", option_id: "marital_history.children_and_guardianship_status.no_children", }, }), ).toBe(false); // Case 3: Divorced AND has children -> true expect( ruleMatches(rule, { "marital_history.current_marital_status": { value: "divorced_after_living_together", option_id: "marital_history.current_marital_status.divorced_after_living_together", }, "marital_history.children_and_guardianship_status": { value: "have_children_living_with_me", option_id: "marital_history.children_and_guardianship_status.have_children_living_with_me", }, }), ).toBe(true); }); it("should evaluate audience rules by gender and age", () => { const questionForMen: QuestionField = { ...dummyQuestion, id: "education_career.ability_to_support_marriage_expenses", audience: { genders: ["male"], }, }; const questionForWomen: QuestionField = { ...dummyQuestion, id: "beliefs_lifestyle.makeup_in_public", audience: { genders: ["female"], }, }; // Male user expect(isQuestionVisible(questionForMen, {}, { gender: "male" })).toBe(true); expect(isQuestionVisible(questionForWomen, {}, { gender: "male" })).toBe(false); // Female user expect(isQuestionVisible(questionForMen, {}, { gender: "female" })).toBe(false); expect(isQuestionVisible(questionForWomen, {}, { gender: "female" })).toBe(true); }); it("should evaluate dynamic requirement via requiredWhen", () => { const q: QuestionField = { ...dummyQuestion, required: false, baseRequired: false, requiredWhen: { parent_question_id: "family_background.parents_marital_status", trigger_option_ids: [ "family_background.parents_marital_status.i_have_special_family_circumstances_and_will_provide_the_details_in_the_description", ], operator: "any_of", }, conditionalRule: { parent_question_id: "family_background.parents_marital_status", trigger_option_ids: [ "family_background.parents_marital_status.i_have_special_family_circumstances_and_will_provide_the_details_in_the_description", ], operator: "any_of", }, }; // When condition not met -> not visible, not required expect(isQuestionVisible(q, {})).toBe(false); expect(isQuestionRequired(q, {})).toBe(false); // When condition met -> visible AND required const matchingAnswers = { "family_background.parents_marital_status": { value: "i_have_special_family_circumstances_and_will_provide_the_details_in_the_description", option_id: "family_background.parents_marital_status.i_have_special_family_circumstances_and_will_provide_the_details_in_the_description", }, }; expect(isQuestionVisible(q, matchingAnswers)).toBe(true); expect(isQuestionRequired(q, matchingAnswers)).toBe(true); }); it("should evaluate representative questions as optional for men and conditional for women based on age", () => { const repNameQuestion: QuestionField = { ...dummyQuestion, id: "contact_residence.representative_s_full_name", title: "Representative's Full Name", required: false, baseRequired: false, requiredWhen: { genders: ["female"], maxAge: 26, }, }; const repPhoneQuestion: QuestionField = { ...dummyQuestion, id: "contact_residence.representative_s_contact_number", title: "Representative's Contact Number", type: "phone", required: false, baseRequired: false, requiredWhen: { genders: ["female"], maxAge: 26, }, }; const repRelQuestion: QuestionField = { ...dummyQuestion, id: "contact_residence.relationship_to_representative", title: "Relationship to Representative", type: "dropdown", required: false, baseRequired: false, requiredWhen: { genders: ["female"], maxAge: 26, }, }; const repQuestions = [repNameQuestion, repPhoneQuestion, repRelQuestion]; // For Male (regardless of age: 20, 26, 30): ALWAYS OPTIONAL for (const age of [18, 20, 25, 26, 27, 35]) { const maleContext = { gender: "male", age }; for (const question of repQuestions) { expect(isQuestionRequired(question, {}, maleContext)).toBe(false); } } // For Female <= 26: REQUIRED for (const age of [18, 20, 25, 26]) { const youngFemaleContext = { gender: "female", age }; for (const question of repQuestions) { expect(isQuestionRequired(question, {}, youngFemaleContext)).toBe(true); } } // For Female >= 27: OPTIONAL for (const age of [27, 30, 35]) { const olderFemaleContext = { gender: "female", age }; for (const question of repQuestions) { expect(isQuestionRequired(question, {}, olderFemaleContext)).toBe(false); } } // When gender is unknown: OPTIONAL for (const question of repQuestions) { expect(isQuestionRequired(question, {}, {})).toBe(false); } }); it("should correctly handle physical health 4-option visibility and medication requirement", () => { // 1. Physical Health Description const physicalHealthDescription: QuestionField = { ...dummyQuestion, id: "appearance_health.physical_health_description", title: "Physical Health Description", required: false, baseRequired: false, visibility: { parent_question_id: "appearance_health.physical_health_status", trigger_option_ids: [ "appearance_health.physical_health_status.i_have_a_specific_or_chronic_illness", "appearance_health.physical_health_status.i_have_a_disability_or_physical_limitation", "appearance_health.physical_health_status.i_have_other_illness_or_physical_conditions", ], operator: "any_of", }, requiredWhen: { parent_question_id: "appearance_health.physical_health_status", trigger_option_ids: [ "appearance_health.physical_health_status.i_have_a_specific_or_chronic_illness", "appearance_health.physical_health_status.i_have_a_disability_or_physical_limitation", "appearance_health.physical_health_status.i_have_other_illness_or_physical_conditions", ], operator: "any_of", }, }; // Option A: No illness -> hidden and not required const optionAAnswers = { "appearance_health.physical_health_status": { value: "i_currently_have_no_specific_illness_or_physical_limitation", option_id: "appearance_health.physical_health_status.i_currently_have_no_specific_illness_or_physical_limitation", }, }; expect(isQuestionVisible(physicalHealthDescription, optionAAnswers)).toBe(false); expect(isQuestionRequired(physicalHealthDescription, optionAAnswers)).toBe(false); // Options B, C, D -> visible and required const optionBAnswers = { "appearance_health.physical_health_status": { value: "i_have_a_specific_or_chronic_illness", option_id: "appearance_health.physical_health_status.i_have_a_specific_or_chronic_illness", }, }; expect(isQuestionVisible(physicalHealthDescription, optionBAnswers)).toBe(true); expect(isQuestionRequired(physicalHealthDescription, optionBAnswers)).toBe(true); const optionCAnswers = { "appearance_health.physical_health_status": { value: "i_have_a_disability_or_physical_limitation", option_id: "appearance_health.physical_health_status.i_have_a_disability_or_physical_limitation", }, }; expect(isQuestionVisible(physicalHealthDescription, optionCAnswers)).toBe(true); expect(isQuestionRequired(physicalHealthDescription, optionCAnswers)).toBe(true); const optionDAnswers = { "appearance_health.physical_health_status": { value: "i_have_other_illness_or_physical_conditions", option_id: "appearance_health.physical_health_status.i_have_other_illness_or_physical_conditions", }, }; expect(isQuestionVisible(physicalHealthDescription, optionDAnswers)).toBe(true); expect(isQuestionRequired(physicalHealthDescription, optionDAnswers)).toBe(true); // 2. Medication Name and Reason const medicationDescription: QuestionField = { ...dummyQuestion, id: "appearance_health.medication_name_and_reason_for_use", title: "Medication Name and Reason for Use", required: false, baseRequired: false, visibility: { parent_question_id: "appearance_health.do_you_currently_take_any_medication_on_an_ongoing_basis", trigger_option_ids: [ "appearance_health.do_you_currently_take_any_medication_on_an_ongoing_basis.yes", ], operator: "any_of", conditions: [ { parent_question_id: "appearance_health.are_you_currently_taking_medication_for_mental_health_related_issues", trigger_option_ids: [ "appearance_health.are_you_currently_taking_medication_for_mental_health_related_issues.yes", ], operator: "any_of", }, ], root_operator: "any_of", }, requiredWhen: { parent_question_id: "appearance_health.do_you_currently_take_any_medication_on_an_ongoing_basis", trigger_option_ids: [ "appearance_health.do_you_currently_take_any_medication_on_an_ongoing_basis.yes", ], operator: "any_of", conditions: [ { parent_question_id: "appearance_health.are_you_currently_taking_medication_for_mental_health_related_issues", trigger_option_ids: [ "appearance_health.are_you_currently_taking_medication_for_mental_health_related_issues.yes", ], operator: "any_of", }, ], root_operator: "any_of", }, }; // Both No -> hidden const medNoAnswers = { "appearance_health.do_you_currently_take_any_medication_on_an_ongoing_basis": { value: "no", option_id: "appearance_health.do_you_currently_take_any_medication_on_an_ongoing_basis.no", }, "appearance_health.are_you_currently_taking_medication_for_mental_health_related_issues": { value: "no", option_id: "appearance_health.are_you_currently_taking_medication_for_mental_health_related_issues.no", }, }; expect(isQuestionVisible(medicationDescription, medNoAnswers)).toBe(false); expect(isQuestionRequired(medicationDescription, medNoAnswers)).toBe(false); // Mental health med Yes -> visible and required const medMentalYesAnswers = { "appearance_health.do_you_currently_take_any_medication_on_an_ongoing_basis": { value: "no", option_id: "appearance_health.do_you_currently_take_any_medication_on_an_ongoing_basis.no", }, "appearance_health.are_you_currently_taking_medication_for_mental_health_related_issues": { value: "yes", option_id: "appearance_health.are_you_currently_taking_medication_for_mental_health_related_issues.yes", }, }; expect(isQuestionVisible(medicationDescription, medMentalYesAnswers)).toBe(true); expect(isQuestionRequired(medicationDescription, medMentalYesAnswers)).toBe(true); // Ongoing med Yes -> visible and required const medOngoingYesAnswers = { "appearance_health.do_you_currently_take_any_medication_on_an_ongoing_basis": { value: "yes", option_id: "appearance_health.do_you_currently_take_any_medication_on_an_ongoing_basis.yes", }, "appearance_health.are_you_currently_taking_medication_for_mental_health_related_issues": { value: "no", option_id: "appearance_health.are_you_currently_taking_medication_for_mental_health_related_issues.no", }, }; expect(isQuestionVisible(medicationDescription, medOngoingYesAnswers)).toBe(true); expect(isQuestionRequired(medicationDescription, medOngoingYesAnswers)).toBe(true); }); it("should hide job title, work location, and monthly income for student, unemployed, student & job seeking, and homemaker", () => { const jobTitle: QuestionField = { ...dummyQuestion, id: "education_career.job_title", title: "عنوان شغلی", visibility: { parent_question_id: "education_career.employment_status", trigger_option_ids: [ "education_career.employment_status.full_time_employed", "education_career.employment_status.part_time_employed", "education_career.employment_status.self_employed_freelancer", "education_career.employment_status.entrepreneur_business_owner", "education_career.employment_status.working_student", "education_career.employment_status.retired", ], operator: "any_of", }, }; const workLocation: QuestionField = { ...dummyQuestion, id: "education_career.work_location", title: "محل فعالیت", visibility: { parent_question_id: "education_career.employment_status", trigger_option_ids: [ "education_career.employment_status.full_time_employed", "education_career.employment_status.part_time_employed", "education_career.employment_status.self_employed_freelancer", "education_career.employment_status.entrepreneur_business_owner", "education_career.employment_status.working_student", ], operator: "any_of", }, }; const monthlyIncome: QuestionField = { ...dummyQuestion, id: "education_career.monthly_income", title: "میزان درآمد ماهانه", required: false, visibility: { parent_question_id: "education_career.employment_status", trigger_option_ids: [ "education_career.employment_status.full_time_employed", "education_career.employment_status.part_time_employed", "education_career.employment_status.self_employed_freelancer", "education_career.employment_status.entrepreneur_business_owner", "education_career.employment_status.working_student", "education_career.employment_status.retired", ], operator: "any_of", }, requiredWhen: { parent_question_id: "education_career.employment_status", trigger_option_ids: [ "education_career.employment_status.full_time_employed", "education_career.employment_status.part_time_employed", "education_career.employment_status.self_employed_freelancer", "education_career.employment_status.entrepreneur_business_owner", "education_career.employment_status.working_student", "education_career.employment_status.retired", ], operator: "any_of", }, }; // Hidden cases: دانشجو, دانشجو و جویای کار, جویای کار / بیکار, خانه‌دار const hiddenStatuses = [ "student", "student_and_job_seeking", "job_seeking_unemployed", "homemaker", ]; for (const status of hiddenStatuses) { const answers = { "education_career.employment_status": { value: status, option_id: `education_career.employment_status.${status}`, }, }; expect(isQuestionVisible(jobTitle, answers)).toBe(false); expect(isQuestionVisible(workLocation, answers)).toBe(false); expect(isQuestionVisible(monthlyIncome, answers)).toBe(false); expect(isQuestionRequired(monthlyIncome, answers)).toBe(false); } // Visible case: Full time employed const fullTimeAnswers = { "education_career.employment_status": { value: "full_time_employed", option_id: "education_career.employment_status.full_time_employed", }, }; expect(isQuestionVisible(jobTitle, fullTimeAnswers)).toBe(true); expect(isQuestionVisible(workLocation, fullTimeAnswers)).toBe(true); expect(isQuestionVisible(monthlyIncome, fullTimeAnswers)).toBe(true); expect(isQuestionRequired(monthlyIncome, fullTimeAnswers)).toBe(true); // Visible case: Working student const workingStudentAnswers = { "education_career.employment_status": { value: "working_student", option_id: "education_career.employment_status.working_student", }, }; expect(isQuestionVisible(jobTitle, workingStudentAnswers)).toBe(true); expect(isQuestionVisible(workLocation, workingStudentAnswers)).toBe(true); expect(isQuestionVisible(monthlyIncome, workingStudentAnswers)).toBe(true); expect(isQuestionRequired(monthlyIncome, workingStudentAnswers)).toBe(true); // Retired case: Job title and monthly income visible, work location hidden const retiredAnswers = { "education_career.employment_status": { value: "retired", option_id: "education_career.employment_status.retired", }, }; expect(isQuestionVisible(jobTitle, retiredAnswers)).toBe(true); expect(isQuestionVisible(workLocation, retiredAnswers)).toBe(false); expect(isQuestionVisible(monthlyIncome, retiredAnswers)).toBe(true); expect(isQuestionRequired(monthlyIncome, retiredAnswers)).toBe(true); }); it("should show parents marital status only when both parents are alive (Option A) and hide for options B, C, D", () => { const parentsMaritalStatus: QuestionField = { ...dummyQuestion, id: "family_background.parents_marital_status", title: "وضعیت تأهل والدین", required: false, visibility: { parent_question_id: "family_background.parents_survival_status", trigger_option_ids: [ "family_background.parents_survival_status.both_parents_are_alive", ], operator: "any_of", }, requiredWhen: { parent_question_id: "family_background.parents_survival_status", trigger_option_ids: [ "family_background.parents_survival_status.both_parents_are_alive", ], operator: "any_of", }, }; // Option A: Both parents are alive -> Visible & Required const optionAAnswers = { "family_background.parents_survival_status": { value: "both_parents_are_alive", option_id: "family_background.parents_survival_status.both_parents_are_alive", }, }; expect(isQuestionVisible(parentsMaritalStatus, optionAAnswers)).toBe(true); expect(isQuestionRequired(parentsMaritalStatus, optionAAnswers)).toBe(true); // Option B: Father has passed away -> Hidden & Not Required const optionBAnswers = { "family_background.parents_survival_status": { value: "father_has_passed_away", option_id: "family_background.parents_survival_status.father_has_passed_away", }, }; expect(isQuestionVisible(parentsMaritalStatus, optionBAnswers)).toBe(false); expect(isQuestionRequired(parentsMaritalStatus, optionBAnswers)).toBe(false); // Option C: Mother has passed away -> Hidden & Not Required const optionCAnswers = { "family_background.parents_survival_status": { value: "mother_has_passed_away", option_id: "family_background.parents_survival_status.mother_has_passed_away", }, }; expect(isQuestionVisible(parentsMaritalStatus, optionCAnswers)).toBe(false); expect(isQuestionRequired(parentsMaritalStatus, optionCAnswers)).toBe(false); // Option D: Both parents have passed away -> Hidden & Not Required const optionDAnswers = { "family_background.parents_survival_status": { value: "both_parents_have_passed_away", option_id: "family_background.parents_survival_status.both_parents_have_passed_away", }, }; expect(isQuestionVisible(parentsMaritalStatus, optionDAnswers)).toBe(false); expect(isQuestionRequired(parentsMaritalStatus, optionDAnswers)).toBe(false); }); it("should show family responsibility follow-up questions (live with you, additional details) and make them required only when responsibility options are selected", () => { const parentId = "family_background.do_you_currently_have_an_ongoing_financial_caregiving_or_guardianship_responsibility_for_a_family_member"; const triggerOptionIds = [ `${parentId}.i_am_responsible_for_caring_for_my_father`, `${parentId}.i_am_responsible_for_caring_for_my_mother`, `${parentId}.i_am_responsible_for_caring_for_both_parents`, `${parentId}.i_am_responsible_for_caring_for_a_sibling_brother_sister`, `${parentId}.i_am_the_legal_guardian_or_supervisor_of_a_family_member`, `${parentId}.i_regularly_provide_financial_support_for_a_family_member_s_living_expenses`, `${parentId}.i_have_other_circumstances_and_will_explain_in_the_description`, `${parentId}.i_am_responsible_for_caring_for_my_parent_s_father_mother_or_both`, ]; const liveWithYou: QuestionField = { ...dummyQuestion, id: "family_background.do_the_supported_individual_s_live_with_you", title: "آیا فرد یا افراد تحت حمایت با شما زندگی می‌کنند؟", required: false, visibility: { parent_question_id: parentId, trigger_option_ids: triggerOptionIds, operator: "any_of", }, requiredWhen: { parent_question_id: parentId, trigger_option_ids: triggerOptionIds, operator: "any_of", }, }; const additionalDetails: QuestionField = { ...dummyQuestion, id: "family_background.additional_details_about_family_responsibility", title: "توضیحات تکمیلی درباره مسئولیت خانوادگی", required: false, visibility: { parent_question_id: parentId, trigger_option_ids: triggerOptionIds, operator: "any_of", }, requiredWhen: { parent_question_id: parentId, trigger_option_ids: triggerOptionIds, operator: "any_of", }, }; // Case 1: No ongoing responsibility selected -> Hidden & Not Required const noRespAnswers = { [parentId]: { value: ["خیر، مسئولیت مستمری ندارم."], option_id: [`${parentId}.no_i_do_not_have_any_ongoing_responsibility`], }, }; expect(isQuestionVisible(liveWithYou, noRespAnswers)).toBe(false); expect(isQuestionRequired(liveWithYou, noRespAnswers)).toBe(false); expect(isQuestionVisible(additionalDetails, noRespAnswers)).toBe(false); expect(isQuestionRequired(additionalDetails, noRespAnswers)).toBe(false); // Case 2: Responsibility selected (e.g. Caring for father) -> Visible & Required const fatherRespAnswers = { [parentId]: { value: ["مسئولیت مراقبت از پدر را بر عهده دارم."], option_id: [`${parentId}.i_am_responsible_for_caring_for_my_father`], }, }; expect(isQuestionVisible(liveWithYou, fatherRespAnswers)).toBe(true); expect(isQuestionRequired(liveWithYou, fatherRespAnswers)).toBe(true); expect(isQuestionVisible(additionalDetails, fatherRespAnswers)).toBe(true); expect(isQuestionRequired(additionalDetails, fatherRespAnswers)).toBe(true); // Case 3: Multiple responsibilities selected -> Visible & Required const multiRespAnswers = { [parentId]: { value: ["مسئولیت مراقبت از پدر", "حمایت مالی"], option_id: [ `${parentId}.i_am_responsible_for_caring_for_my_father`, `${parentId}.i_regularly_provide_financial_support_for_a_family_member_s_living_expenses`, ], }, }; expect(isQuestionVisible(liveWithYou, multiRespAnswers)).toBe(true); expect(isQuestionRequired(liveWithYou, multiRespAnswers)).toBe(true); expect(isQuestionVisible(additionalDetails, multiRespAnswers)).toBe(true); expect(isQuestionRequired(additionalDetails, multiRespAnswers)).toBe(true); }); it("should handle Section 6 marital history and children follow-up rules properly", () => { const mStatusId = "marital_history.current_marital_status"; const previousTriggerIds = [ `${mStatusId}.failed_engagement_annulled_marriage_without_living_together`, `${mStatusId}.failed_marriage_contract_annulled_engagement_without_starting_joint_life`, `${mStatusId}.divorced_after_living_together`, `${mStatusId}.widowed`, ]; const divorcedOrAnnulledIds = [ `${mStatusId}.failed_engagement_annulled_marriage_without_living_together`, `${mStatusId}.failed_marriage_contract_annulled_engagement_without_starting_joint_life`, `${mStatusId}.divorced_after_living_together`, ]; const cStatusId = "marital_history.children_and_guardianship_status"; const hasChildrenTriggerIds = [ `${cStatusId}.have_children_living_with_me`, `${cStatusId}.have_children_not_living_with_me`, ]; const prevDuration: QuestionField = { ...dummyQuestion, id: "marital_history.previous_marriage_duration", title: "مدت ازدواج یا عقد قبلی", required: false, visibility: { parent_question_id: mStatusId, trigger_option_ids: previousTriggerIds, operator: "any_of" }, requiredWhen: { parent_question_id: mStatusId, trigger_option_ids: previousTriggerIds, operator: "any_of" }, }; const reasonSeparation: QuestionField = { ...dummyQuestion, id: "marital_history.reason_for_separation", title: "علت جدایی، در صورت وجود", required: false, visibility: { parent_question_id: mStatusId, trigger_option_ids: divorcedOrAnnulledIds, operator: "any_of" }, }; const childrenStatus: QuestionField = { ...dummyQuestion, id: "marital_history.children_and_guardianship_status", title: "وضعیت فرزند و تکفل", required: false, visibility: { parent_question_id: mStatusId, trigger_option_ids: previousTriggerIds, operator: "any_of" }, requiredWhen: { parent_question_id: mStatusId, trigger_option_ids: previousTriggerIds, operator: "any_of" }, }; const numChildren: QuestionField = { ...dummyQuestion, id: "marital_history.number_of_children", title: "تعداد فرزندان", required: false, visibility: { parent_question_id: cStatusId, trigger_option_ids: hasChildrenTriggerIds, operator: "any_of" }, requiredWhen: { parent_question_id: cStatusId, trigger_option_ids: hasChildrenTriggerIds, operator: "any_of" }, }; const custodyStatus: QuestionField = { ...dummyQuestion, id: "marital_history.what_is_the_custody_status_of_your_child_ren", title: "وضعیت حضانت فرزند یا فرزندان شما چگونه است؟", required: false, visibility: { parent_question_id: cStatusId, trigger_option_ids: hasChildrenTriggerIds, operator: "any_of" }, requiredWhen: { parent_question_id: cStatusId, trigger_option_ids: hasChildrenTriggerIds, operator: "any_of" }, }; // 1. Single: previous duration, reason for separation, and children status are all HIDDEN const singleAns = { [mStatusId]: { value: "single_never_married", option_id: `${mStatusId}.single_never_married`, }, }; expect(isQuestionVisible(prevDuration, singleAns)).toBe(false); expect(isQuestionVisible(reasonSeparation, singleAns)).toBe(false); expect(isQuestionVisible(childrenStatus, singleAns)).toBe(false); // 2. Divorced: duration, reason, and children status are all VISIBLE const divorcedAns = { [mStatusId]: { value: "divorced_after_living_together", option_id: `${mStatusId}.divorced_after_living_together`, }, }; expect(isQuestionVisible(prevDuration, divorcedAns)).toBe(true); expect(isQuestionRequired(prevDuration, divorcedAns)).toBe(true); expect(isQuestionVisible(reasonSeparation, divorcedAns)).toBe(true); expect(isQuestionVisible(childrenStatus, divorcedAns)).toBe(true); expect(isQuestionRequired(childrenStatus, divorcedAns)).toBe(true); // 3. Widowed: duration & children VISIBLE, reason for separation HIDDEN const widowedAns = { [mStatusId]: { value: "widowed", option_id: `${mStatusId}.widowed`, }, }; expect(isQuestionVisible(prevDuration, widowedAns)).toBe(true); expect(isQuestionRequired(prevDuration, widowedAns)).toBe(true); expect(isQuestionVisible(reasonSeparation, widowedAns)).toBe(false); expect(isQuestionVisible(childrenStatus, widowedAns)).toBe(true); expect(isQuestionRequired(childrenStatus, widowedAns)).toBe(true); // 4. Children follow-ups: when children exist -> VISIBLE & REQUIRED const hasChildAns = { [cStatusId]: { value: "have_children_living_with_me", option_id: `${cStatusId}.have_children_living_with_me`, }, }; expect(isQuestionVisible(numChildren, hasChildAns)).toBe(true); expect(isQuestionRequired(numChildren, hasChildAns)).toBe(true); expect(isQuestionVisible(custodyStatus, hasChildAns)).toBe(true); expect(isQuestionRequired(custodyStatus, hasChildAns)).toBe(true); // 5. No children: child questions HIDDEN & NOT REQUIRED const noChildAns = { [cStatusId]: { value: "no_children", option_id: `${cStatusId}.no_children`, }, }; expect(isQuestionVisible(numChildren, noChildAns)).toBe(false); expect(isQuestionRequired(numChildren, noChildAns)).toBe(false); expect(isQuestionVisible(custodyStatus, noChildAns)).toBe(false); expect(isQuestionRequired(custodyStatus, noChildAns)).toBe(false); }); });