def test_do_not_go_to_next_question_for_date_answer(self): goto_rule = { "id": "next-question", "when": [{ "id": "date-answer", "condition": "equals", "date_comparison": { "value": "2018-01" }, }], } answer_store = AnswerStore({}) answer_store.add_or_update( Answer(answer_id="date-answer", value="2018-02-01")) current_location = Location(section_id="some-section", block_id="some-block") self.assertFalse( evaluate_goto( goto_rule=goto_rule, schema=get_schema_mock(), metadata={}, answer_store=answer_store, list_store=ListStore(), current_location=current_location, ))
def relationship_answer_store(): answer_store = AnswerStore() answer_store.add_or_update( Answer( answer_id="relationship-answer", value=[ { "list_item_id": "abc123", "to_list_item_id": "xyz987", "relationship": "Husband or Wife", }, { "list_item_id": "abc123", "to_list_item_id": "123abc", "relationship": "Son or Daughter", }, { "list_item_id": "xyz987", "to_list_item_id": "123abc", "relationship": "Son or Daughter", }, ], ) ) return answer_store
def test_minimum_and_maximum_offset_dates(app, value_source_resolver, rule_evaluator): value_source_resolver.metadata = {"date": "2018-02-20"} answer_store = AnswerStore() test_answer_id = Answer(answer_id="date", value="2018-03-20") answer_store.add_or_update(test_answer_id) value_source_resolver.answer_store = answer_store answer = { "id": "date_answer", "type": "Date", "minimum": { "value": {"identifier": "date", "source": "metadata"}, "offset_by": {"days": -10}, }, "maximum": { "value": {"identifier": "date", "source": "answers"}, "offset_by": {"years": 1}, }, } handler = DateHandler(answer, value_source_resolver, rule_evaluator, error_messages) minimum_date = handler.get_date_value("minimum") maximum_date = handler.get_date_value("maximum") assert minimum_date == parse_datetime("2018-02-10") assert maximum_date == parse_datetime("2019-03-20")
def test_get_referenced_offset_value_with_list_item_id(app, schema_mock): list_item_id = "abcde" answer_store = AnswerStore() test_answer_id = Answer(answer_id="date", value="2018-03-20", list_item_id=list_item_id) location = Location(section_id="test", list_item_id=list_item_id) answer_store.add_or_update(test_answer_id) answer = { "maximum": { "value": { "identifier": "date", "source": "answers" }, "offset_by": { "months": 1 }, } } handler = DateHandler(answer, answer_store=answer_store, location=location) maximum_date = handler.get_date_value("maximum") assert maximum_date == convert_to_datetime("2018-04-20")
def test_evaluate_when_rule_fetches_answer_using_list_item_id(self): when = { "when": [{ "id": "my_answer", "condition": "equals", "value": "an answer" }] } list_item_id = "abc123" answer_store = AnswerStore() answer_store.add_or_update( Answer(answer_id="my_answer", value="an answer", list_item_id=list_item_id)) current_location = Location(section_id="some-section", block_id="some-block", list_item_id=list_item_id) schema = Mock(get_schema()) schema.get_list_item_id_for_answer_id = Mock(return_value=list_item_id) self.assertTrue( evaluate_when_rules( when_rules=when["when"], schema=schema, metadata={}, answer_store=answer_store, list_store=ListStore(), current_location=current_location, ))
def test_get_referenced_offset_value_with_list_item_id( app, value_source_resolver, rule_evaluator ): list_item_id = "abcde" test_answer_id = Answer( answer_id="date", value="2018-03-20", list_item_id=list_item_id ) location = Location(section_id="test", list_item_id=list_item_id) answer_store = AnswerStore() answer_store.add_or_update(test_answer_id) value_source_resolver.answer_store = answer_store value_source_resolver.location = location value_source_resolver.list_item_id = list_item_id answer = { "maximum": { "value": {"identifier": "date", "source": "answers"}, "offset_by": {"months": 1}, } } handler = DateHandler(answer, value_source_resolver, rule_evaluator, error_messages) maximum_date = handler.get_date_value("maximum") assert maximum_date == parse_datetime("2018-04-20")
def test_get_routing_path_when_first_block_in_group_skipped(self): # Given schema = load_schema_from_name("test_skip_condition_group") answer_store = AnswerStore() answer_store.add_or_update( Answer(answer_id="do-you-want-to-skip-answer", value="Yes")) # When path_finder = PathFinder(schema, answer_store, self.list_store, self.progress_store, self.metadata) # Then expected_route = [ { "block_id": "do-you-want-to-skip-block", "group_id": "do-you-want-to-skip-group", }, { "block_id": "summary", "group_id": "should-skip-group" }, ] section_id = schema.get_section_id_for_block_id("summary") pytest.xfail( reason= "Known bug when skipping last group due to summary bundled into it" ) self.assertEqual(path_finder.routing_path(section_id=section_id), expected_route)
def test_meta_comparison_missing(self): # Given goto_rule = { "id": "next-question", "when": [{ "condition": "equals", "meta": "variant_flags.does_not_exist.does_not_exist", "value": True, }], } answer_store = AnswerStore() answer_store.add_or_update(Answer(answer_id="my_answer", value="Yes")) metadata = {"varient_flags": {"sexual_identity": True}} current_location = Location(section_id="some-section", block_id="some-block") self.assertFalse( evaluate_goto( goto_rule=goto_rule, schema=get_schema(), metadata=metadata, answer_store=answer_store, list_store=ListStore(), current_location=current_location, ))
def test_should_not_go_to_next_question_when_second_condition_fails(self): # Given goto_rule = { "id": "next-question", "when": [ { "id": "my_answer", "condition": "equals", "value": "Yes" }, { "condition": "equals", "meta": "sexual_identity", "value": False }, ], } answer_store = AnswerStore() answer_store.add_or_update(Answer(answer_id="my_answer", value="Yes")) metadata = {"sexual_identity": True} current_location = Location(section_id="some-section", block_id="some-block") self.assertFalse( evaluate_goto( goto_rule=goto_rule, schema=get_schema(), metadata=metadata, answer_store=answer_store, list_store=ListStore(), current_location=current_location, ))
def test_do_not_go_to_next_question_for_multiple_answers(self): # Given goto_rule = { "id": "next-question", "when": [ { "id": "my_answer", "condition": "equals", "value": "Yes" }, { "id": "my_other_answer", "condition": "equals", "value": "2" }, ], } answer_store = AnswerStore() answer_store.add_or_update(Answer(answer_id="my_answer", value="No")) current_location = Location(section_id="some-section", block_id="some-block") self.assertFalse( evaluate_goto( goto_rule=goto_rule, schema=get_schema(), metadata={}, answer_store=answer_store, list_store=ListStore(), current_location=current_location, ))
def test_evaluate_goto_returns_true_when_answer_value_not_equals_any_match_values( self, ): goto = { "id": "next-question", "when": [{ "id": "my_answers", "condition": "not equals any", "values": ["answer1", "answer2"], }], } answer_store = AnswerStore() answer_store.add_or_update( Answer(answer_id="my_answers", value="answer3")) current_location = Location(section_id="some-section", block_id="some-block") self.assertTrue( evaluate_goto( goto_rule=goto, schema=get_schema(), metadata={}, answer_store=answer_store, list_store=ListStore(), current_location=current_location, ))
def test_sum_calculated_field(self): store = AnswerStore() answer_total = Answer(answer_id="total-answer", value=10) store.add_or_update(answer_total) with self.app_request_context(): schema = load_schema_from_name("test_sum_equal_validation_against_total") question_schema = schema.get_block("breakdown-block").get("question") form_data = MultiDict( { "breakdown-1": "", "breakdown-2": "5", "breakdown-3": "4", "breakdown-4": "1", } ) expected_form_data = { "csrf_token": "", "breakdown-1": None, "breakdown-2": Decimal("5"), "breakdown-3": Decimal("4"), "breakdown-4": Decimal("1"), } form = generate_form( schema, question_schema, store, metadata=None, form_data=form_data ) form.validate() self.assertEqual(form.data, expected_form_data)
def test_evaluate_when_rule_with_invalid_list_item_id(self): when = { "when": [{ "id": "my_answer", "condition": "equals", "value": "an answer" }] } answer_store = AnswerStore() answer_store.add_or_update( Answer(answer_id="my_answer", value="an answer", list_item_id="abc123")) current_location = Location(section_id="some-section", block_id="some-block", list_item_id="123abc") schema = Mock(get_schema()) schema.is_repeating_answer = Mock(return_value=False) self.assertFalse( evaluate_when_rules( when_rules=when["when"], schema=schema, metadata={}, answer_store=answer_store, list_store=ListStore(), current_location=current_location, ))
def test_generate_form_with_title_and_no_answer_label(self): """ Checks that the form is still generated when there is no answer label but there is a question title """ store = AnswerStore() conditional_answer = Answer(answer_id="behalf-of-answer", value="chad") store.add_or_update(conditional_answer) with self.app_request_context(): schema = load_schema_from_name("test_title") question_schema = schema.get_block("single-title-block").get("question") form_data = MultiDict({"feeling-answer": "good"}) expected_form_data = {"csrf_token": "", "feeling-answer": "good"} with patch( "app.questionnaire.path_finder.evaluate_goto", return_value=False ): form = generate_form( schema, question_schema, store, metadata={}, form_data=form_data ) form.validate() assert form.data == expected_form_data
def test_choose_content_to_display(content_variant_schema): schema = QuestionnaireSchema(content_variant_schema) answer_store = AnswerStore({}) answer_store.add_or_update(Answer(answer_id="age-answer", value="18")) metadata = {} block = schema.get_block("block1") section_id = schema.get_section_id_for_block_id(block["id"]) content_to_display = choose_content_to_display( schema.get_block("block1"), schema, metadata, answer_store, ListStore({}), Location(section_id=section_id, block_id=block["id"]), ) assert content_to_display[0]["title"] == "You are over 16" answer_store = AnswerStore({}) content_to_display = choose_content_to_display( schema.get_block("block1"), schema, metadata, answer_store, ListStore({}), Location(section_id=section_id, block_id=block["id"]), ) assert content_to_display[0]["title"] == "You are ageless"
def test_routing_path_empty_routing_rules(self): schema = load_schema_from_name("test_checkbox") section_id = schema.get_section_id_for_block_id("mandatory-checkbox") expected_path = RoutingPath( [ "mandatory-checkbox", "non-mandatory-checkbox", "single-checkbox" ], section_id="default-section", ) answer_1 = Answer(answer_id="mandatory-checkbox-answer", value="Cheese") answer_2 = Answer(answer_id="non-mandatory-checkbox-answer", value="deep pan") answer_3 = Answer(answer_id="single-checkbox-answer", value="Estimate") answer_store = AnswerStore() answer_store.add_or_update(answer_1) answer_store.add_or_update(answer_2) answer_store.add_or_update(answer_3) progress_store = ProgressStore([{ "section_id": "default-section", "list_item_id": None, "status": CompletionStatus.COMPLETED, "block_ids": ["mandatory-checkbox"], }]) path_finder = PathFinder(schema, answer_store, self.list_store, progress_store, self.metadata) routing_path = path_finder.routing_path(section_id=section_id) self.assertEqual(routing_path, expected_path)
def test_routing_path_should_not_skip_group(self): # Given schema = load_schema_from_name("test_skip_condition_group") section_id = schema.get_section_id_for_block_id("do-you-want-to-skip") answer_store = AnswerStore() answer_store.add_or_update( Answer(answer_id="do-you-want-to-skip-answer", value="No")) progress_store = ProgressStore([{ "section_id": "default-section", "list_item_id": None, "status": CompletionStatus.COMPLETED, "block_ids": ["do-you-want-to-skip"], }]) # When path_finder = PathFinder(schema, answer_store, self.list_store, progress_store, self.metadata) routing_path = path_finder.routing_path(section_id=section_id) # Then expected_routing_path = RoutingPath( [ "do-you-want-to-skip", "should-skip", "last-group-block", "summary" ], section_id="default-section", ) with patch("app.questionnaire.path_finder.evaluate_skip_conditions", return_value=False): self.assertEqual(routing_path, expected_routing_path)
def test_get_routing_path_when_first_block_in_group_skipped(self): # Given schema = load_schema_from_name("test_skip_condition_group") answer_store = AnswerStore() answer_store.add_or_update( Answer(answer_id="do-you-want-to-skip-answer", value="Yes")) # When path_finder = PathFinder( schema, answer_store, self.list_store, self.progress_store, self.metadata, self.response_metadata, ) # Then expected_route = RoutingPath( section_id="default-section", block_ids=["do-you-want-to-skip"], ) self.assertEqual( expected_route, path_finder.routing_path(section_id="default-section"), )
def test_routing_path_with_conditional_path(self): schema = load_schema_from_name("test_new_routing_number_equals") section_id = schema.get_section_id_for_block_id("number-question") expected_path = RoutingPath( ["number-question", "correct-answer"], section_id="default-section", ) answer = Answer(answer_id="answer", value=123) answer_store = AnswerStore() answer_store.add_or_update(answer) progress_store = ProgressStore([{ "section_id": "default-section", "list_item_id": None, "status": CompletionStatus.COMPLETED, "block_ids": ["number-question"], }]) path_finder = PathFinder( schema, answer_store, self.list_store, progress_store, self.metadata, self.response_metadata, ) routing_path = path_finder.routing_path(section_id=section_id) self.assertEqual(routing_path, expected_path)
def test_new_routing_basic_and_conditional_path(self): # Given schema = load_schema_from_name("test_new_routing_number_equals") section_id = schema.get_section_id_for_block_id("number-question") expected_path = RoutingPath( ["number-question", "correct-answer"], section_id="default-section", ) answer_1 = Answer(answer_id="answer", value=123) answer_store = AnswerStore() answer_store.add_or_update(answer_1) # When path_finder = PathFinder( schema, answer_store, self.list_store, self.progress_store, self.metadata, self.response_metadata, ) routing_path = path_finder.routing_path(section_id=section_id) # Then self.assertEqual(routing_path, expected_path)
def test_new_routing_path_should_skip_group(self): # Given schema = load_schema_from_name("test_new_skip_condition_group") section_id = schema.get_section_id_for_block_id("do-you-want-to-skip") answer_store = AnswerStore() answer_store.add_or_update( Answer(answer_id="do-you-want-to-skip-answer", value="Yes")) progress_store = ProgressStore([{ "section_id": "default-section", "list_item_id": None, "status": CompletionStatus.COMPLETED, "block_ids": ["do-you-want-to-skip"], }]) # When path_finder = PathFinder( schema, answer_store, self.list_store, progress_store, self.metadata, self.response_metadata, ) routing_path = path_finder.routing_path(section_id=section_id) # Then expected_routing_path = RoutingPath( ["do-you-want-to-skip"], section_id="default-section", ) self.assertEqual(routing_path, expected_routing_path)
def test_transform_variants_with_question_variants(question_variant_schema): schema = QuestionnaireSchema(question_variant_schema) answer_store = AnswerStore({}) answer_store.add_or_update(Answer(answer_id="when-answer", value="no")) metadata = {} block = schema.get_block("block1") section_id = schema.get_section_id_for_block_id(block["id"]) transformed_block = transform_variants( block, schema, metadata, answer_store, ListStore({}), Location(section_id=section_id, block_id=block["id"]), ) compare_transformed_block(block, transformed_block, "Question 1, No") answer_store.add_or_update(Answer(answer_id="when-answer", value="yes")) transformed_block = transform_variants( block, schema, metadata, answer_store, ListStore({}), Location(section_id=section_id, block_id=block["id"]), ) compare_transformed_block(block, transformed_block, "Question 1, Yes")
def test_choose_question_to_display(question_variant_schema): schema = QuestionnaireSchema(question_variant_schema) answer_store = AnswerStore({}) answer_store.add_or_update(Answer(answer_id="when-answer", value="yes")) metadata = {} block = schema.get_block("block1") section_id = schema.get_section_id_for_block_id(block["id"]) question_to_display = choose_question_to_display( schema.get_block("block1"), schema, metadata, answer_store, ListStore({}), Location(section_id=section_id, block_id=block["id"]), ) assert question_to_display["title"] == "Question 1, Yes" answer_store = AnswerStore({}) question_to_display = choose_question_to_display( schema.get_block("block1"), schema, metadata, answer_store, ListStore({}), Location(section_id=section_id, block_id=block["id"]), ) assert question_to_display["title"] == "Question 1, No"
def test_bespoke_message_for_sum_validation(self): store = AnswerStore() answer_total = Answer(answer_id="total-answer", value=10) store.add_or_update(answer_total) with self.app_request_context(): schema = load_schema_from_name("test_sum_equal_validation_against_total") question_schema = QuestionnaireSchema.get_mutable_deepcopy( schema.get_block("breakdown-block").get("question") ) question_schema["validation"] = { "messages": {"TOTAL_SUM_NOT_EQUALS": "Test Message"} } form_data = MultiDict({"breakdown-1": "3", "breakdown-2": "5"}) form = generate_form( schema, question_schema, store, metadata=None, form_data=form_data ) with patch( "app.questionnaire.questionnaire_schema.QuestionnaireSchema.get_all_questions_for_block", return_value=[question_schema], ): form.validate() self.assertIn( form.question_errors["breakdown-question"], "Test Message" )
def test_invalid_date_range_and_single_date_periods(self): with self.app_request_context(): store = AnswerStore() test_answer_id = Answer(answer_id="date", value="2017-03-20") store.add_or_update(test_answer_id) schema = load_schema_from_name("test_date_validation_range") question_schema = { "id": "date-range-question", "type": "DateRange", "answers": [ { "id": "date-range-from", "label": "Period from", "mandatory": True, "type": "Date", "minimum": {"value": "2018-01-10", "offset_by": {"days": -5}}, }, { "id": "date-range-to", "label": "Period to", "mandatory": True, "type": "Date", "maximum": { "value": {"identifier": "date", "source": "answers"}, "offset_by": {"days": 5}, }, }, ], } form_data = MultiDict( { "date-range-from-day": "6", "date-range-from-month": "1", "date-range-from-year": "2018", "date-range-to-day": "15", "date-range-to-month": "1", "date-range-to-year": "2018", } ) metadata = {"schema_name": "test_date_validation_range"} form = generate_form( schema, question_schema, store, metadata=metadata, form_data=form_data ) with self.assertRaises(Exception) as ite: with patch( "app.questionnaire.questionnaire_schema.QuestionnaireSchema.get_all_questions_for_block", return_value=[question_schema], ): form.validate() self.assertEqual( "The schema has invalid date answer limits for date-range-question", str(ite.exception), )
def test_remove_answer_and_block_if_routing_backwards(self): schema = load_schema_from_name("test_confirmation_question") section_id = schema.get_section_id_for_block_id( "confirm-zero-employees-block") # All blocks completed progress_store = ProgressStore([{ "section_id": "default-section", "list_item_id": None, "status": CompletionStatus.COMPLETED, "block_ids": [ "number-of-employees-total-block", "confirm-zero-employees-block", ], }]) answer_store = AnswerStore() number_of_employees_answer = Answer( answer_id="number-of-employees-total", value=0) confirm_zero_answer = Answer(answer_id="confirm-zero-employees-answer", value="No I need to change this") answer_store.add_or_update(number_of_employees_answer) answer_store.add_or_update(confirm_zero_answer) path_finder = PathFinder(schema, answer_store, self.list_store, progress_store, self.metadata) self.assertEqual( len( path_finder.progress_store.get_completed_block_ids( section_id="default-section")), 2, ) self.assertEqual(len(path_finder.answer_store), 2) routing_path = path_finder.routing_path(section_id=section_id) expected_path = RoutingPath( [ "number-of-employees-total-block", "confirm-zero-employees-block", "number-of-employees-total-block", ], section_id="default-section", ) self.assertEqual(routing_path, expected_path) self.assertEqual( path_finder.progress_store.get_completed_block_ids( section_id="default-section"), [ progress_store.get_completed_block_ids( section_id="default-section")[0] ], ) self.assertEqual(len(path_finder.answer_store), 1)
def test_transform_variants_list_collector(list_collector_variant_schema): schema = QuestionnaireSchema(list_collector_variant_schema) answer_store = AnswerStore({}) answer_store.add_or_update(Answer(answer_id="when-answer", value="no")) metadata = {} response_metadata = {} block = schema.get_block("block1") section_id = schema.get_section_id_for_block_id(block["id"]) transformed_block = transform_variants( block, schema, metadata, response_metadata, answer_store, ListStore({}), Location(section_id=section_id, block_id=block["id"]), ) compare_transformed_block( block["add_block"], transformed_block["add_block"], "Add, No" ) compare_transformed_block( block["remove_block"], transformed_block["remove_block"], "Remove, No" ) compare_transformed_block( block["edit_block"], transformed_block["edit_block"], "Edit, No" ) answer_store.add_or_update(Answer(answer_id="when-answer", value="yes")) transformed_block = transform_variants( block, schema, metadata, response_metadata, answer_store, ListStore({}), Location(section_id=section_id, block_id=block["id"]), ) compare_transformed_block( block["add_block"], transformed_block["add_block"], "Add, Yes" ) compare_transformed_block( block["remove_block"], transformed_block["remove_block"], "Remove, Yes" ) compare_transformed_block( block["edit_block"], transformed_block["edit_block"], "Edit, Yes" )
def test_multi_calculation(self): store = AnswerStore() answer_total = Answer(answer_id="total-answer", value=10) store.add_or_update(answer_total) with self.app_request_context(): schema = load_schema_from_name("test_sum_multi_validation_against_total") question_schema = schema.get_block("breakdown-block").get("question") form_data = MultiDict( { "breakdown-1": "", "breakdown-2": "", "breakdown-3": "", "breakdown-4": "", } ) # With no answers question validation should pass form = generate_form( schema, question_schema, store, metadata=None, form_data=form_data ) form.validate() self.assertEqual(len(form.question_errors), 0) # With the data equaling the total question validation should pass form_data["breakdown-1"] = "10" form = generate_form( schema, question_schema, store, metadata=None, form_data=form_data ) form.validate() self.assertEqual(len(form.question_errors), 0) # With the data not equaling zero or the total, question validation should fail form_data["breakdown-1"] = "1" form = generate_form( schema, question_schema, store, metadata=None, form_data=form_data ) form.validate() self.assertEqual( form.question_errors["breakdown-question"], schema.error_messages["TOTAL_SUM_NOT_EQUALS"] % {"total": "10"}, )
def test_get_referenced_offset_value_for_answer_id( app, value_source_resolver, rule_evaluator ): answer_store = AnswerStore() test_answer_id = Answer(answer_id="date", value="2018-03-20") answer_store.add_or_update(test_answer_id) value_source_resolver.answer_store = answer_store answer = {"maximum": {"value": {"identifier": "date", "source": "answers"}}} handler = DateHandler(answer, value_source_resolver, rule_evaluator, error_messages) maximum_date = handler.get_date_value("maximum") maximum_date = handler.transform_date_by_offset(maximum_date, {"months": 1}) assert maximum_date == parse_datetime("2018-04-20")
def test_build_path_with_group_routing(self): # Given i have answered the routing question schema = load_schema_from_name("test_routing_group") section_id = schema.get_section_id_for_block_id("group2-block") answer_store = AnswerStore() answer_store.add_or_update( Answer(answer_id="which-group-answer", value="group2")) # When i build the path path_finder = PathFinder(schema, answer_store, self.list_store, self.progress_store, self.metadata) path = path_finder.routing_path(section_id=section_id) # Then it should route me straight to Group2 and not Group1 self.assertNotIn("group1-block", path) self.assertIn("group2-block", path)