def test_option_has_other(self): with self.app_request_context(): # STANDARD CHECKBOX schema = load_schema_from_params('test', 'checkbox') block_json = schema.get_block('mandatory-checkbox') form = generate_form(schema, block_json, AnswerStore(), metadata=None, group_instance=0, formdata={}) self.assertFalse( form.option_has_other('mandatory-checkbox-answer', 1)) self.assertTrue( form.option_has_other('mandatory-checkbox-answer', 6)) # MUTUALLY EXCLUSIVE CHECKBOX schema = load_schema_from_params('test', 'checkbox_mutually_exclusive') block_json = schema.get_block('mandatory-checkbox') form = generate_form(schema, block_json, AnswerStore(), metadata=None, group_instance=0, formdata={}) self.assertFalse( form.option_has_other('mandatory-checkbox-answer', 1)) self.assertTrue( form.option_has_other('mandatory-checkbox-answer', 5))
def form(self): question_json = self.rendered_block.get("question") if self._form_data: return generate_form( self._schema, question_json, self._questionnaire_store.answer_store, self._questionnaire_store.list_store, self._questionnaire_store.metadata, self._questionnaire_store.response_metadata, self._current_location, form_data=self._form_data, ) answers = self._get_answers_for_question(question_json) return generate_form( self._schema, question_json, self._questionnaire_store.answer_store, self._questionnaire_store.list_store, self._questionnaire_store.metadata, self._questionnaire_store.response_metadata, self._current_location, data=answers, )
def test_detail_answer_mandatory_only_checked_if_option_selected(self): # The detail_answer can only be mandatory if the option it is associated with is answered with self.app_request_context(): schema = load_schema_from_name("test_checkbox_detail_answer_multiple") question_schema = schema.get_block("mandatory-checkbox").get("question") # Option is selected therefore the detail answer should be mandatory (schema defined) form = generate_form( schema, question_schema, AnswerStore(), metadata=None, form_data=MultiDict({"mandatory-checkbox-answer": "Your choice"}), ) detail_answer_field = getattr(form, "your-choice-answer-mandatory") self.assertIsInstance(detail_answer_field.validators[0], ResponseRequired) # Option not selected therefore the detail answer should not be mandatory form = generate_form( schema, question_schema, AnswerStore(), metadata=None, data={"mandatory-checkbox-answer": "Ham"}, ) detail_answer_field = getattr(form, "your-choice-answer-mandatory") self.assertEqual(detail_answer_field.validators, ())
def test_get_other_answer_invalid(self): with self.app_request_context(): # STANDARD CHECKBOX schema = load_schema_from_params('test', 'checkbox') block_json = schema.get_block('mandatory-checkbox') form = generate_form( schema, block_json, AnswerStore(), metadata=None, group_instance=0, formdata={'other-answer-mandatory': 'Some data'}) field = form.get_other_answer('mandatory-checkbox-answer', 4) self.assertEqual(None, field) # MUTUALLY EXCLUSIVE CHECKBOX schema = load_schema_from_params('test', 'checkbox_mutually_exclusive') block_json = schema.get_block('mandatory-checkbox') form = generate_form( schema, block_json, AnswerStore(), metadata=None, group_instance=0, formdata={'other-answer-mandatory': 'Some data'}) field = form.get_other_answer('mandatory-checkbox-answer', 4) self.assertEqual(None, field)
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") data = { "breakdown-1": "", "breakdown-2": "", "breakdown-3": "", "breakdown-4": "", } # With no answers question validation should pass form = generate_form(schema, question_schema, store, metadata=None, formdata=data) form.validate() self.assertEqual(len(form.question_errors), 0) # With the data equaling the total question validation should pass data["breakdown-1"] = "10" form = generate_form(schema, question_schema, store, metadata=None, formdata=data) form.validate() self.assertEqual(len(form.question_errors), 0) # With the data not equaling zero or the total, question validation should fail data["breakdown-1"] = "1" form = generate_form(schema, question_schema, store, metadata=None, formdata=data) form.validate() self.assertEqual( form.question_errors["breakdown-question"], schema.error_messages["TOTAL_SUM_NOT_EQUALS"] % dict(total="10"), )
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_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_form_date_range_populates_data(self): with self.test_request_context(): survey = load_schema_file("1_0102.json") block_json = SchemaHelper.get_block(survey, "reporting-period") error_messages = SchemaHelper.get_messages(survey) data = { 'period-from-day': '01', 'period-from-month': '3', 'period-from-year': '2016', 'period-to-day': '31', 'period-to-month': '3', 'period-to-year': '2016' } expected_form_data = { 'csrf_token': '', 'period-from': { 'day': '01', 'month': '3', 'year': '2016' }, 'period-to': { 'day': '31', 'month': '3', 'year': '2016' } } form = generate_form(block_json, data, error_messages) self.assertEqual(form.data, expected_form_data)
def get_form_for_location(schema, block_json, location, answer_store, metadata, disable_mandatory=False): # pylint: disable=too-many-locals """ Returns the form necessary for the location given a get request, plus any template arguments :param schema: schema :param block_json: The block json :param location: The location which this form is for :param answer_store: The current answer store :param metadata: metadata :param disable_mandatory: Make mandatory answers optional :return: form, template_args A tuple containing the form for this location and any additional template arguments """ if disable_mandatory: block_json = disable_mandatory_answers(block_json) mapped_answers = get_mapped_answers(schema, answer_store, location=location) return generate_form( schema, block_json.get("question"), answer_store, metadata, location=location, data=mapped_answers, )
def test_date_yyyy_combined_range_too_large_validation(self): with self.app_request_context(): schema = load_schema_from_params('test', 'date_validation_yyyy_combined') block_json = schema.get_block('date-range-block') data = { 'date-range-from-year': '2016', 'date-range-to-year': '2020' } metadata = { 'ref_p_start_date': '2017-01-01', 'ref_p_end_date': '2017-02-12' } expected_form_data = { 'csrf_token': '', 'date-range-from': '2016', 'date-range-to': '2020' } form = generate_form(schema, block_json, AnswerStore(), metadata, group_instance=0, formdata=data) form.validate() self.assertEqual(form.data, expected_form_data) self.assertEqual( form.question_errors['date-range-question'], schema.error_messages['DATE_PERIOD_TOO_LARGE'] % dict(max='3 years'))
def test_bespoke_message_for_date_validation_range(self): with self.app_request_context(): schema = load_schema_from_name("test_date_validation_range") question_schema = schema.get_block("date-range-block").get( "question") question_schema = { "id": "date-range-question", "type": "DateRange", "validation": { "messages": { "DATE_PERIOD_TOO_SMALL": "Test Message" } }, "period_limits": { "minimum": { "days": 20 } }, "answers": [ { "id": "date-range-from", "label": "Period from", "mandatory": True, "type": "Date", }, { "id": "date-range-to", "label": "Period to", "mandatory": True, "type": "Date", }, ], } data = { "date-range-from-day": "25", "date-range-from-month": "1", "date-range-from-year": "2018", "date-range-to-day": "26", "date-range-to-month": "1", "date-range-to-year": "2018", } form = generate_form(schema, question_schema, AnswerStore(), metadata=None, formdata=data) with patch( "app.questionnaire.questionnaire_schema.QuestionnaireSchema.get_all_questions_for_block", return_value=[question_schema], ): form.validate() self.assertIn(form.question_errors["date-range-question"], "Test Message")
def test_mandatory_mutually_exclusive_question_raises_error_with_question_text( self, ): with self.app_request_context(): schema = load_schema_from_name("test_question_title_in_error") question_schema = schema.get_block("mutually-exclusive-checkbox").get( "question" ) answer_store = AnswerStore( [{"answer_id": "mandatory-checkbox-answer", "value": ["Tuna"]}] ) renderer = PlaceholderRenderer( language="en", schema=schema, answer_store=answer_store ) rendered_schema = renderer.render(question_schema, None) form = generate_form( schema, rendered_schema, answer_store, metadata=None, form_data=MultiDict(), ) form.validate_mutually_exclusive_question(question_schema) error = form.question_errors["mutually-exclusive-checkbox-question"] assert error == format_message_with_title( error_messages["MANDATORY_CHECKBOX"], "Did you really answer ‘Tuna’ to the previous question?", )
def test_answer_with_detail_answer_errors_are_correctly_mapped(self): with self.app_request_context(): schema = load_schema_from_name( "test_radio_mandatory_with_detail_answer_mandatory" ) question_schema = schema.get_block("radio-mandatory").get("question") form = generate_form( schema, question_schema, AnswerStore(), metadata=None, form_data=MultiDict({"radio-mandatory-answer": "Other"}), ) form.validate() mapped_errors = form.map_errors() self.assertTrue( self._error_exists( "radio-mandatory-answer", schema.error_messages["MANDATORY_TEXTFIELD"], mapped_errors, ) ) self.assertFalse( self._error_exists( "other-answer-mandatory", schema.error_messages["MANDATORY_TEXTFIELD"], mapped_errors, ) )
def test_form_subfield_errors_are_correctly_mapped(self): with self.app_request_context(): schema = load_schema_from_name("test_date_range") question_schema = schema.get_block("date-block").get("question") form = generate_form(schema, question_schema, AnswerStore(), metadata=None) form.validate() mapped_errors = form.map_errors() self.assertTrue( self._error_exists( "date-range-from-answer", schema.error_messages["MANDATORY_DATE"], mapped_errors, ) ) self.assertTrue( self._error_exists( "date-range-to-answer", schema.error_messages["MANDATORY_DATE"], mapped_errors, ) )
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_date_range_to_precedes_from_raises_question_error(self): with self.app_request_context(): schema = load_schema_from_name("test_date_range") question_schema = schema.get_block("date-block").get("question") data = { "date-range-from-answer-day": "25", "date-range-from-answer-month": "12", "date-range-from-answer-year": "2016", "date-range-to-answer-day": "24", "date-range-to-answer-month": "12", "date-range-to-answer-year": "2016", } expected_form_data = { "csrf_token": "", "date-range-from-answer": "2016-12-25", "date-range-to-answer": "2016-12-24", } form = generate_form(schema, question_schema, AnswerStore(), metadata=None, formdata=data) form.validate() self.assertEqual(form.data, expected_form_data) self.assertEqual( form.question_errors["date-range-question"], schema.error_messages["INVALID_DATE_RANGE"], AnswerStore(), )
def test_answer_with_child_errors_are_correctly_mapped(self): with self.app_request_context(): schema = load_schema_from_params( 'test', 'radio_mandatory_with_mandatory_other') block_json = schema.get_block('radio-mandatory') form = generate_form(schema, block_json, AnswerStore(), metadata=None, group_instance=0, formdata={'radio-mandatory-answer': 'Other'}) form.validate() mapped_errors = form.map_errors() self.assertTrue( self._error_exists( 'radio-mandatory-answer', schema.error_messages['MANDATORY_TEXTFIELD'], mapped_errors)) self.assertFalse( self._error_exists( 'other-answer-mandatory', schema.error_messages['MANDATORY_TEXTFIELD'], mapped_errors))
def test_date_range_valid_period(self): with self.app_request_context(): schema = load_schema_from_params('test', 'date_validation_range') block_json = schema.get_block('date-range-block') data = { 'date-range-from-day': '25', 'date-range-from-month': '12', 'date-range-from-year': '2016', 'date-range-to-day': '26', 'date-range-to-month': '01', 'date-range-to-year': '2017' } expected_form_data = { 'csrf_token': '', 'date-range-from': '2016-12-25', 'date-range-to': '2017-01-26' } form = generate_form(schema, block_json, AnswerStore(), metadata=None, group_instance=0, formdata=data) form.validate() self.assertEqual(form.data, expected_form_data)
def test_mutually_exclusive_question_raises_error_when_both_answered(self): with self.app_request_context(): schema = load_schema_from_name("test_mutually_exclusive") question_schema = schema.get_block("mutually-exclusive-date").get( "question" ) form_data = MultiDict( { "date-answer-day": "17", "date-answer-month": "9", "date-answer-year": "2018", "date-exclusive-answer": "I prefer not to say", } ) form = generate_form( schema, question_schema, AnswerStore(), metadata=None, form_data=form_data, ) form.validate_mutually_exclusive_question(question_schema) self.assertEqual( form.question_errors["mutually-exclusive-date-question"], error_messages["MUTUALLY_EXCLUSIVE"], )
def test_date_range_too_small_period_raises_question_error(self): with self.app_request_context(): schema = load_schema_from_name("test_date_validation_range") question_schema = schema.get_block("date-range-block").get( "question") data = { "date-range-from-day": "25", "date-range-from-month": "12", "date-range-from-year": "2016", "date-range-to-day": "26", "date-range-to-month": "12", "date-range-to-year": "2016", } expected_form_data = { "csrf_token": "", "date-range-from": "2016-12-25", "date-range-to": "2016-12-26", } form = generate_form(schema, question_schema, AnswerStore(), metadata=None, formdata=data) form.validate() self.assertEqual(form.data, expected_form_data) self.assertEqual( form.question_errors["date-range-question"], schema.error_messages["DATE_PERIOD_TOO_SMALL"] % dict(min="23 days"), AnswerStore(), )
def test_date_range_too_small_period_raises_question_error(self): with self.app_request_context(): schema = load_schema_from_params('test', 'date_validation_range') block_json = schema.get_block('date-range-block') data = { 'date-range-from-day': '25', 'date-range-from-month': '12', 'date-range-from-year': '2016', 'date-range-to-day': '26', 'date-range-to-month': '12', 'date-range-to-year': '2016' } expected_form_data = { 'csrf_token': '', 'date-range-from': '2016-12-25', 'date-range-to': '2016-12-26' } form = generate_form(schema, block_json, AnswerStore(), metadata=None, group_instance=0, formdata=data) form.validate() self.assertEqual(form.data, expected_form_data) self.assertEqual( form.question_errors['date-range-question'], schema.error_messages['DATE_PERIOD_TOO_SMALL'] % dict(min='23 days'), AnswerStore())
def test_date_range_form_with_data(self): with self.app_request_context(): schema = load_schema_from_name("test_date_range") question_schema = schema.get_block("date-block").get("question") form_data = MultiDict( { "date-range-from-answer-day": "1", "date-range-from-answer-month": "05", "date-range-from-answer-year": "2015", "date-range-to-answer-day": "1", "date-range-to-answer-month": "09", "date-range-to-answer-year": "2017", } ) form = generate_form( schema, question_schema, AnswerStore(), metadata=None, form_data=form_data, ) self.assertTrue(hasattr(form, "date-range-from-answer")) self.assertTrue(hasattr(form, "date-range-to-answer")) period_from_field = getattr(form, "date-range-from-answer") period_to_field = getattr(form, "date-range-to-answer") self.assertIsInstance(period_from_field.year.validators[0], DateRequired) self.assertIsInstance(period_to_field.year.validators[0], DateRequired) self.assertEqual(period_from_field.data, "2015-05-01") self.assertEqual(period_to_field.data, "2017-09-01")
def test_form_date_range_populates_data(self): with self.app_request_context(): schema = load_schema_from_params('test', '0102') block_json = schema.get_block('reporting-period') data = { 'period-from-day': '01', 'period-from-month': '3', 'period-from-year': '2016', 'period-to-day': '31', 'period-to-month': '3', 'period-to-year': '2016' } expected_form_data = { 'csrf_token': '', 'period-from': '2016-03-01', 'period-to': '2016-03-31' } form = generate_form(schema, block_json, AnswerStore(), metadata=None, group_instance=0, formdata=data) self.assertEqual(form.data, expected_form_data)
def test_form_for_radio_other_not_selected(self): with self.app_request_context(): schema = load_schema_from_name( "test_radio_mandatory_with_detail_answer_mandatory" ) question_schema = schema.get_block("radio-mandatory").get("question") form_data = MultiDict( { "radio-mandatory-answer": "Bacon", "other-answer-mandatory": "Old other text", } ) form = generate_form( schema, question_schema, AnswerStore(), metadata=None, form_data=form_data, ) self.assertTrue(hasattr(form, "radio-mandatory-answer")) other_text_field = getattr(form, "other-answer-mandatory") self.assertEqual(other_text_field.data, "")
def test_date_range_to_precedes_from_raises_question_error(self): with self.app_request_context(): schema = load_schema_from_params('test', '0102') block_json = schema.get_block('reporting-period') data = { 'period-from-day': '25', 'period-from-month': '12', 'period-from-year': '2016', 'period-to-day': '24', 'period-to-month': '12', 'period-to-year': '2016' } expected_form_data = { 'csrf_token': '', 'period-from': '2016-12-25', 'period-to': '2016-12-24' } form = generate_form(schema, block_json, AnswerStore(), metadata=None, group_instance=0, formdata=data) form.validate() self.assertEqual(form.data, expected_form_data) self.assertEqual(form.question_errors['reporting-period-question'], schema.error_messages['INVALID_DATE_RANGE'], AnswerStore())
def test_date_range_valid_period(self): with self.app_request_context(): schema = load_schema_from_name("test_date_validation_range") question_schema = schema.get_block("date-range-block")["question"] form_data = MultiDict( { "date-range-from-day": "25", "date-range-from-month": "12", "date-range-from-year": "2016", "date-range-to-day": "26", "date-range-to-month": "01", "date-range-to-year": "2017", } ) expected_form_data = { "csrf_token": "", "date-range-from": "2016-12-25", "date-range-to": "2017-01-26", } form = generate_form( schema, question_schema, AnswerStore(), metadata=None, form_data=form_data, ) form.validate() self.assertEqual(form.data, expected_form_data)
def post_form_for_block( schema, block_json, answer_store, metadata, request_form, location, disable_mandatory=False, ): """ Returns the form necessary for the location given a post request, plus any template arguments :param block_json: The block json :param location: The location which this form is for :param answer_store: The current answer store :param metadata: metadata :param request_form: form, template_args A tuple containing the form for this location and any additional template arguments :param location: The location in the survey this post is for :param disable_mandatory: Make mandatory answers optional """ if disable_mandatory: block_json = disable_mandatory_answers(block_json) question = block_json.get("question") data = clear_detail_answer_field(request_form, question) return generate_form(schema, question, answer_store, metadata, location, formdata=data)
def test_form_date_range_populates_data(self): with self.app_request_context(): schema = load_schema_from_name("test_date_range") question_schema = schema.get_block("date-block").get("question") form_data = MultiDict( { "date-range-from-answer-day": "01", "date-range-from-answer-month": "3", "date-range-from-answer-year": "2016", "date-range-to-answer-day": "31", "date-range-to-answer-month": "3", "date-range-to-answer-year": "2016", } ) expected_form_data = { "csrf_token": "", "date-range-from-answer": "2016-03-01", "date-range-to-answer": "2016-03-31", } form = generate_form( schema, question_schema, AnswerStore(), metadata=None, form_data=form_data, ) self.assertEqual(form.data, expected_form_data)
def post_form_for_location(block_json, location, answer_store, request_form, error_messages, disable_mandatory=False): """ Returns the form necessary for the location given a post request, plus any template arguments :param disable_mandatory: Make mandatory answers optional :param error_messages: The default error messages to use within the form :param block_json: The block json :param location: The location which this form is for :param answer_store: The current answer store :param request_form: form, template_args A tuple containing the form for this location and any additional template arguments """ if disable_mandatory: block_json = disable_mandatory_answers(block_json) if location.block_id == 'household-composition': return generate_household_composition_form(block_json, request_form, error_messages), None elif location.block_id in ['relationships', 'household-relationships']: choices = build_relationship_choices(answer_store, location.group_instance) form = generate_relationship_form(block_json, len(choices), request_form, error_messages) return form, {'relation_instances': choices} else: data = clear_other_text_field( request_form, SchemaHelper.get_questions_for_block(block_json)) return generate_form(block_json, data, error_messages), None
def test_date_yyyy_combined_range_too_large_validation(self): with self.app_request_context(): schema = load_schema_from_name("test_date_validation_yyyy_combined") question_schema = schema.get_block("date-range-block").get("question") form_data = MultiDict( {"date-range-from-year": "2016", "date-range-to-year": "2020"} ) metadata = { "ref_p_start_date": "2017-01-01", "ref_p_end_date": "2017-02-12", } expected_form_data = { "csrf_token": "", "date-range-from": "2016", "date-range-to": "2020", } form = generate_form( schema, question_schema, AnswerStore(), metadata, form_data=form_data ) form.validate() self.assertEqual(form.data, expected_form_data) self.assertEqual( form.question_errors["date-range-question"], schema.error_messages["DATE_PERIOD_TOO_LARGE"] % {"max": "3 years"}, )